#include "PluginManager.h"
#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>

PluginManager* PluginManager::m_instance=nullptr;

class PluginManager::PluginsManagerPrivate
{
public:
    PluginsManagerPrivate()
    {
        m_names.clear();
        m_versions.clear();
        m_dependencies.clear();
        m_loaders.clear();
    }
    ~PluginsManagerPrivate(){}

    QHash<QString, QVariant> m_names;               //插件路径--插件名称
    QHash<QString, QVariant> m_versions;            //插件路径--插件版本
    QHash<QString, QVariantList> m_dependencies;    //插件路径--插件额外依赖的其他插件
    QHash<QString, QPluginLoader *> m_loaders;      //插件路径--QPluginLoader实例

    bool check(const QString &filepath)             //插件依赖检测
    {
        bool status = true;

        foreach (QVariant item, m_dependencies.value(filepath)) {
            QVariantMap map = item.toMap();
            // 依赖的插件名称、版本、路径
            QVariant name = map.value("name");
            QVariant version = map.value("version");
            QString path = m_names.key(name);

            /********** 检测插件是否依赖于其他插件 **********/
            // 先检测插件名称
            if (!m_names.values().contains(name)) {
                qDebug() << Q_FUNC_INFO << "  Missing dependency:" << name.toString() << "for plugin" << path;
                status = false;
                continue;
            }

            // 再检测插件版本
            if (m_versions.value(path) != version) {
                qDebug() << Q_FUNC_INFO << "    Version mismatch:" << name.toString() << "version"
                         << m_versions.value(m_names.key(name)).toString() << "but" << version.toString() << "required for plugin" << path;
                status = false;
                continue;
            }

            // 然后，检测被依赖的插件是否还依赖于另外的插件
            if (!check(path)) {
                qDebug() << Q_FUNC_INFO << "Corrupted dependency:" << name.toString() << "for plugin" << path;
                status = false;
                continue;
            }
        }

        return status;
    }

};


PluginManager::PluginManager(QObject *parent) : QObject(parent)
{
    managerPrivate = new PluginsManagerPrivate;
}
PluginManager::~PluginManager()
{
    delete managerPrivate;
}

void PluginManager::loadAllPlugins()
{
    QDir pluginsDir(qApp->applicationDirPath());    //pluginsDir: "../build-xxx-debug/debug"
    if(pluginsDir.dirName().toLower() == "debug" ||
            pluginsDir.dirName().toLower() == "release") {
        pluginsDir.cdUp();  //pluginsDir: "../build-xxx-debug"
        pluginsDir.cdUp();  //pluginsDir: "../"
    }
    pluginsDir.cd("plugins");

    QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

    //初始化插件中的元数据
    for(QFileInfo fileinfo : pluginsInfo){
        qDebug()<<"loadAllPlugins:"<<fileinfo.absoluteFilePath();
        scanMetaData(fileinfo.absoluteFilePath());
    }

    //加载插件
    for(QFileInfo fileinfo : pluginsInfo)
        loadPlugin(fileinfo.absoluteFilePath());
}

void PluginManager::scanMetaData(const QString &filepath)
{
    //判断是否为库（后缀有效性）
    if(!QLibrary::isLibrary(filepath))
        return ;

    //获取元数据
    QPluginLoader *loader = new QPluginLoader(filepath);

    qDebug()<<loader->metaData().keys();
    QJsonObject json = loader->metaData().value("MetaData").toObject();
    for(int i=0; i<json.keys().size(); ++i) {
        qDebug()<<json.keys().at(i)<< " : "<<json.value(json.keys().at(i));
    }

    managerPrivate->m_names.insert(filepath, json.value("name").toVariant());
    managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());
    managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());

    delete loader;
    loader = nullptr;
}

void PluginManager::loadPlugin(const QString &filepath)
{
    if(!QLibrary::isLibrary(filepath))
        return;

    //检测依赖
    if(!managerPrivate->check(filepath))
        return;

    //加载插件
    QPluginLoader *loader = new QPluginLoader(filepath);
    if(loader->load()) {
        PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
        if(plugin) {
            managerPrivate->m_loaders.insert(filepath, loader);
            connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),
                    this,SLOT(recMsgFromPlugin(PluginMetaData)));
        }else {
            delete loader;
            loader = nullptr;
        }
    }else{
        qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();
    }
}

void PluginManager::unloadAllPlugins()
{
    for(QString filepath : managerPrivate->m_loaders.keys())
        unloadPlugin(filepath);
}

void PluginManager::unloadPlugin(const QString &filepath)
{
    QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);
    //卸载插件，并从内部数据结构中移除
    if(loader->unload()) {
        managerPrivate->m_loaders.remove(filepath);
        delete loader;
        loader = nullptr;
    }
}

QList<QPluginLoader *> PluginManager::allPlugins()
{
    return managerPrivate->m_loaders.values();
}

QList<QVariant> PluginManager::allPluginsName()
{
    return managerPrivate->m_names.values();
}

QVariant PluginManager::getPluginName(QPluginLoader *loader)
{
    if(loader)
        return managerPrivate->m_names.value(managerPrivate->m_loaders.key(loader));
    else
        return "";
}

QPluginLoader *PluginManager::getPlugin(const QString &name)
{
    return managerPrivate->m_loaders.value(managerPrivate->m_names.key(name));
}

void PluginManager::recMsgFromPlugin(PluginMetaData metaData)
{
    auto loader = getPlugin(metaData.dest); //目标插件
    if(loader) {
        auto interface = qobject_cast<PluginInterface*>(loader->instance());
        if(interface) {
            interface->recMsgFromManager(metaData); //转发给对应的插件
        }
    }
}
